/*
Pyxis 2.0
Copyright 2010-2011 Thomas W. Holtquist

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

using System;
using System.Collections;
using System.Threading;

using Microsoft.SPOT;
using Microsoft.SPOT.Hardware;
using Microsoft.SPOT.Presentation.Media;
using Microsoft.SPOT.Net.NetworkInformation;

using GHIElectronics.NETMF.Hardware;

using Skewworks.Pyxis.EXT;

namespace Skewworks.Pyxis.Kernel
{
    public class MenuTray
    {

        #region Constants

        // The maximum with of the menubar will be 4 icons & the time
        private const int TimeWidth = 59;

        #endregion

        #region Variables

        private MenuBar _parent;

        private int _w;
        private int MaxWidth = 147;
        
        private bool _showTime;                             // True when system clock should be shown
        private Thread _oclock;                             // Time updating thread
        private int _lastTime;

        private ArrayList _icons = new ArrayList();         // Collection of MenuTrayIcons
        private bool _modal = false;                        // True if API in modal mode

        private bool _online;                               // True if network connection present
        private bool _drive;                                // True if drives present

        private rect _expand;                               // Contains rect for expand button
        private bool _expanded;                             // True when expanded

        MenuTrayIcon _mtiDrive;
        MenuTrayIcon _mtiNetwork;

        #endregion

        #region Constructor

        internal MenuTray(MenuBar parent)
        {

            // Prepare Time
            if (SettingsManager.BootSettings.EnableRTC || DeviceManager.ActiveDevice == DeviceType.ChipworkX)
            {
                _w = TimeWidth + 4;
                _showTime = true;
                MaxWidth = 143;
            }
            else
            {
                _w = 8;
                MaxWidth = 148;
            }

            // Drive Icon
            if (parent.API.MyDrives.AvailableDrives.Length > 0)
            {
                _drive = true;
                _mtiDrive = new MenuTrayIcon(null, new Image32(Resources.GetBytes(Resources.BinaryResources.hdd)));
                AddIcon(_mtiDrive);
            }
            else
            {
                _mtiDrive = new MenuTrayIcon(null, new Image32(Resources.GetBytes(Resources.BinaryResources.nodrives)));
                AddIcon(_mtiDrive);
            }

            // Network Icon
            if (parent.API.MyNetwork.NetworkConnected)
            {
                _online = true;
                _mtiNetwork = new MenuTrayIcon(null, new Image32(Resources.GetBytes(Resources.BinaryResources.connected)));
                AddIcon(_mtiNetwork);
            }
            else
            {
                _mtiNetwork = new MenuTrayIcon(null, new Image32(Resources.GetBytes(Resources.BinaryResources.disconnected)));
                AddIcon(_mtiNetwork);
            }

            // Start Time
            if (SettingsManager.BootSettings.EnableRTC || DeviceManager.ActiveDevice == DeviceType.ChipworkX)
            {
                try
                {
                    if (DeviceManager.ActiveDevice != DeviceType.Emulator)
                        Utility.SetLocalTime(RealTimeClock.GetTime());
                }
                catch (Exception) { }

                _oclock = new Thread(UpdateTime);
                _oclock.Priority = ThreadPriority.BelowNormal;
                _oclock.Start();
            }

            // Assign Variables
            _parent = parent;

            // Bind Events
            API.MyDrives.DriveAdded += new OnDriveAdded(MyDrives_DriveAdded);
            API.MyDrives.DriveRemoved += new OnDriveRemoved(MyDrives_DriveRemoved);

            API.MyNetwork.ConnectionEstablished += new OnNetworkOnline(MyNetwork_ConnectionEstablished);
            API.MyNetwork.ConnectionRemoved += new OnNetworkOffline(MyNetwork_ConnectionRemoved);

            API.ModalStateChanged += new OnModalStateChanged(API_ModalStateChanged);
            API.DateTimeChanged += new OnDateTimeChanged(api_DateTimeChanged);

        }

        #endregion

        #region Properties

        protected internal PyxisAPI API
        {
            get { return _parent.API; }
        }

        public int Width
        {
            get { return (_w > MaxWidth) ? MaxWidth : _w; }
        }

        #endregion

        #region Public Methods

        public void AddIcon(MenuTrayIcon Icon)
        {
            Icon._parent = this;
            _w += 20;
            _icons.Add(Icon);
            if (_parent != null)
                Render(true);
        }

        public void RemoveIcon(MenuTrayIcon Icon)
        {
            _icons.Remove(Icon);
            if (_parent != null)
                Render(true);
        }

        #endregion

        #region Touch Methods

        internal bool InvokeTouchDown(point e)
        {
            // Check expansion first
            if (_expand.contains(e))
            {
                // Expand Immediately
                if (!_expanded)
                    ShowExpandedIcons();
                else
                    Collapse();

                return true;
            }

            // Check Icons
            MenuTrayIcon ico;
            for (int i = 0; i < _icons.Count; i++)
            {
                ico = (MenuTrayIcon)_icons[i];
                if (ico._bounds.contains(e))
                {
                    if (_expanded && e.Y < 22)
                        Collapse();

                    ico.TouchDown(this, e);

                    return true;
                }
            }

            if (_expanded)
                Collapse();

            return false;
        }

        internal bool InvokeTouchUp(point e)
        {
            bool bRet = false;

            // Check expansion first
            if (_expand.contains(e))
                return bRet = true;

            // Check Icons
            MenuTrayIcon ico;
            for (int i = 0; i < _icons.Count; i++)
            {
                ico = (MenuTrayIcon)_icons[i];
                if (ico._bounds.contains(e))
                {
                    if (_expanded)
                        Collapse();

                    bRet = true;
                }

                ico.TouchUp(this, e);
            }

            return bRet;
        }

        #endregion

        #region GUI

        private void Collapse()
        {
            _expanded = false;
            _parent.API.RefreshScreen();
        }

        public void Render(bool flush = false)
        {
            if (_parent == null)
                return;

            int left = AppearanceManager.ScreenWidth - Width;
            int x = AppearanceManager.ScreenWidth - 4;
            Color DrkGray = ColorUtility.ColorFromRGB(112, 112, 112);
            
            // Draw Bar
            _parent.API.ScreenBuffer.SetClippingRectangle(0, 0, AppearanceManager.ScreenWidth, AppearanceManager.ScreenHeight);
            if (flush) _parent.API.ScreenBuffer.DrawImage(left, 0, _parent._buffer, left, 0, _w, 22);
            _parent.API.ScreenBuffer.DrawRectangle(DrkGray, 0, left, 0, _w, 22, 0, 0, DrkGray, 0, 0, DrkGray, 0, 0, 150);

            // Render Time First
            if (_showTime)
            {
                RenderTime(false);
                x = AppearanceManager.ScreenWidth - TimeWidth;
            }

            // Render Icons
            MenuTrayIcon ico;
            _expand = new rect(0, 0, 0, 0);
            int maxX = AppearanceManager.ScreenWidth - MaxWidth + 4;
            for (int i = 0; i < _icons.Count; i++)
            {
                x -= 20;
                ico = (MenuTrayIcon)_icons[i];

                if (x > maxX || (x == maxX && i == _icons.Count - 1))
                {
                    ico._bounds = new rect(x, 2, 16, 16);
                    ico.Icon.draw(_parent.API.ScreenBuffer, x, 2);
                }
                else if (x == maxX)
                {
                    _expand = new rect(x, 2, 16, 16);
                    _parent.API.ScreenBuffer.DrawImage(x, 2, Resources.GetBitmap(Resources.BitmapResources.traydown), 0, 0, 16, 16);
                    ico._bounds = new rect(0, 0, 16, 16);
                }
                else
                    ico._bounds = new rect(0, 0, 16, 16);
            }

            if (flush)
                _parent.API.ScreenBuffer.Flush(left, 0, _w, 22);
        }

        private void ShowExpandedIcons()
        {
            MenuTrayIcon ico;
            double icos = 0;
            int iStart = 0;

            _expanded = true;

            // Determine number of hidden icons
            for (int i = 0; i < _icons.Count; i++)
            {
                ico = (MenuTrayIcon)_icons[i];
                if (ico._bounds.X == 0)
                {
                    iStart = i;
                    icos = _icons.Count - i;
                    break;
                }
            }

            // Determine the number of rows
            int iRows = (int)System.Math.Ceiling(icos / 3);
            int w = (iRows == 1) ? (int)((icos * 20) + 6) : 66;
            int h = (iRows * 20) + 6;
            int x = _expand.X + 5;
            int y = 27;

            // Render Container
            Color DrkGray = ColorUtility.ColorFromRGB(112, 112, 112);
            _parent.API.ScreenBuffer.DrawRectangle(DrkGray, 1, _expand.X, 22, w, h, 0, 0, Colors.LightGray, 0, 0, Colors.LightGray, 0, 0, 256);

            for (int i = iStart; i < _icons.Count; i++)
            {
                ico = (MenuTrayIcon)_icons[i];
                ico._bounds = new rect(x, y, 16, 16);
                ico.Icon.draw(_parent.API.ScreenBuffer, x, y);
                x += 20;
                if (x == _expand.X + 65)
                {
                    x = _expand.X + 5;
                    y += 20;
                }
            }

            _parent.API.ScreenBuffer.Flush(_expand.X, 22, w, h);
        }

        #endregion

        #region Private Methods

        private string GetFormattedTime()
        {
            int h = DateTime.Now.Hour;
            int m = DateTime.Now.Minute;
            string AP;

            if (h < 12)
            {
                AP = " AM";
            }
            else
            {
                AP = " PM";
                h -= 12;
            }

            if (h == 0) h = 12;
            return h + ":" + IntToXXString(m) + AP;
        }

        private string IntToXXString(int value)
        {
            string val = value.ToString();
            if (val.Length == 2) return val;
            return "0" + val;
        }

        private void RenderTime(bool flush = true)
        {
            if (_modal || _parent == null)
            {
                _lastTime = (DateTime.Now.Hour * 100) + DateTime.Now.Minute;
                return;
            }

            if (_parent._buffer == null)
                return;

            Color DrkGray = ColorUtility.ColorFromRGB(112, 112, 112);

            rect Bounds = new rect(AppearanceManager.ScreenWidth - TimeWidth, 3, TimeWidth - 4, 19);

            // Draw Bar
            _parent.API.ScreenBuffer.DrawImage(Bounds.X, Bounds.Y, _parent._buffer, Bounds.X, Bounds.Y, Bounds.Width, 19);
            _parent.API.ScreenBuffer.DrawRectangle(DrkGray, 0, Bounds.X, 3, Bounds.Width, 19, 0, 0, DrkGray, 0, 0, DrkGray, 0, 0, 150);
            _parent.API.ScreenBuffer.DrawTextInRect(GetFormattedTime(), Bounds.X, 3, Bounds.Width - 4, 19, Bitmap.DT_AlignmentRight, Colors.White, FontManager.Arial);

            _lastTime = (DateTime.Now.Hour * 100) + DateTime.Now.Minute;

            if (flush)
                _parent.API.ScreenBuffer.Flush(Bounds.X, Bounds.Y, Bounds.Width, 19);
        }

        private void UpdateTime()
        {
            while (true)
            {
                Thread.Sleep(1000);
                if ((DateTime.Now.Hour * 100) + DateTime.Now.Minute != _lastTime)
                    RenderTime();
            }
        }

        #endregion

        #region Drive Events

        /// <summary>
        /// Handle drives being added
        /// </summary>
        /// <param name="root">Root of new drive</param>
        private void MyDrives_DriveAdded(string root)
        {
            if (_drive) return;
            _drive = true;
            _mtiDrive.Icon = new Image32(Resources.GetBytes(Resources.BinaryResources.hdd));
        }

        /// <summary>
        /// Handle removal of drives 
        /// </summary>
        /// <param name="root">Root of drive removed</param>
        private void MyDrives_DriveRemoved(string root)
        {
            if (API.MyDrives.AvailableDrives.Length > 0) return;
            _drive = false;
            _mtiDrive.Icon = new Image32(Resources.GetBytes(Resources.BinaryResources.nodrives));
        }

        #endregion

        #region Network Events

        private void MyNetwork_ConnectionEstablished()
        {
            if (_online) return;
            _online = true;
            _mtiNetwork.Icon = new Image32(Resources.GetBytes(Resources.BinaryResources.connected));
        }

        private void MyNetwork_ConnectionRemoved()
        {
            if (API.MyNetwork.NetworkConnected) return;
            if (!_online) return;
            _online = false;
            _mtiNetwork.Icon = new Image32(Resources.GetBytes(Resources.BinaryResources.disconnected));
        }

        #endregion

        #region API Events

        private void api_DateTimeChanged()
        {
            _lastTime = 0;
            RenderTime();
        }

        private void API_ModalStateChanged(bool IsModalActive)
        {
            _modal = IsModalActive;
            if (!_modal && _showTime)
                _lastTime = 0;
        }

        #endregion

    }
}
